#include <string.h>

#include "lib.h"

#include "FiveCardHand.h"
#include "CardMgr.h"
#include "SevenCardStudRanks.h"


FiveCardHand::FiveCardHand(CardMgr* Mgr)
	: Hand(Mgr)
{
	highCard = 0;
	handRank = 0xff;
}

int32
FiveCardHand::outrank(uint8* Buf)
{
	*Buf = (uint8)(handRank & 0xff);
	return 1;
}

int32
FiveCardHand::outhigh(uint8* Buf)
{
	int32 used = 0;

	mgr->encode(highCard, Buf, &used);

	return used;
}

bool8
FiveCardHand::rankHand()
{
	int32 i;
	uint8* probe, *first;
	uint8 rank;
	bool8 flush = true;

	memset(counts, 0, 13 * sizeof(uint8));

	for (i = 0 ; i < getCount(); i++)
	{
		rank = mgr->getRank(getCardAt(i));

		if (rank >= 2)
		{
			++counts[rank - 2];

			if (i > 0 && flush)
				flush = (bool8)(
					mgr->getSuit(getCardAt(i)) == mgr->getSuit(getCardAt(i - 1)));
		}
	}

	if (memchr(counts, 5, 13))
	{
		handRank = HR_5OFAKIND;
		return true;
	}


	// pair, 2 pairs, 3-of-a-kind, 4-of-a-kind, full house
	//
	if (memchr(counts, 4, 13))
	{
		handRank = HR_4OFAKIND;
		return true;
	}

	if (memchr(counts, 3, 13))
	{
		if (memchr(counts, 2, 13))
		{
			handRank = HR_FULLHOUSE;
			return true;
		}
		else
		{
			handRank = HR_3OFAKIND;
			return true;
		}
	}

	probe = (uint8*)memchr(counts, 2, 13);
	if (probe)
	{
		if (probe < counts+(13-1) && memchr(probe+1, 2, 13 - (probe-counts+1)))
		{
			handRank = HR_2PAIR;
			return true;
		}
		else
		{
			handRank = HR_PAIR;
			return true;
		}
	}

	// straights:  ace counts high or low; ace,2,3,4,5 is lower than 2,3,4,5,6
	//
	first = probe = (uint8*)memchr(counts, 1, 13);
	if (first)
	{
		i = 0;
		while (probe < counts + 13)
		{
			if (*probe++ == 1)
				i++;
			else
				break;
		}

		if (i == 5)
		{
			if (flush)
				if (first == counts + 10-2)
				{
					handRank = HR_ROYALFLUSH;
					return true;
				}
				else
				{
					handRank = HR_STRAIGHTFLUSH;
					return true;
				}

			handRank = HR_STRAIGHT;
			return true;
		}

		if (i == 4 &&								// ace-low straights
			counts[CardMgr::ACE-2] && first == counts)
		{
			if (flush)
			{
				handRank = HR_STRAIGHTFLUSH;
				return true;
			}

			handRank = HR_STRAIGHT;
			return true;
		}

		handRank = flush ? HR_FLUSH : HR_HIGHCARD;
		return true;
	}
	else
		handRank = HR_HIGHCARD;

	return false;
}

void
FiveCardHand::calcNthHighest(int32 n)
{
	highCard = getNthHighest(n, handRank);
}

uint8
FiveCardHand::getNthHighest(int32 n, int32 r)
{
	uint8 *probe;

	switch (r)
	{
		case HR_5OFAKIND:
			return (uint8)(((uint8*)memchr(counts, 5, 13) - counts) + 2);

		case HR_4OFAKIND:
			if (n < 4)
				return (uint8)(((uint8*)memchr(counts, 4, 13) - counts) + 2);
			return getNthHighest(n, HR_HIGHCARD);

		case HR_3OFAKIND:
			if (n < 3)
				return (uint8)(((uint8*)memchr(counts, 3, 13) - counts) + 2);
			return getNthHighest(n, HR_HIGHCARD);
				
		case HR_FULLHOUSE:
			if (n < 3)
				return getNthHighest(n, HR_3OFAKIND); 
			return (uint8)(((uint8*)memchr(counts, 2, 13) - counts) + 2);

		case HR_2PAIR:
			if (n < 4)
			{
				probe = (uint8*)memchr(counts, 2, 13);

				if (n < 2)
				{
					uint8* probe2;

					probe2 = (uint8*)memchr(probe+1, 2, 13 - (probe-counts+1));

					return (uint8)((probe2 - counts) + 2);
				}

				return (uint8)((probe - counts) + 2);
			}
			return getNthHighest(n, HR_HIGHCARD);

		case HR_PAIR:
			if (n < 2)
				return (uint8)(((uint8*)memchr(counts, 2, 13) - counts) + 2);
			return getNthHighest(n, HR_HIGHCARD);

		case HR_ROYALFLUSH:
		case HR_STRAIGHTFLUSH:
		case HR_STRAIGHT:
		{
			bool8 aceLowStraight;
			int32 i;

			aceLowStraight = (bool8)(counts[CardMgr::ACE-2] && counts[5-2]);

			probe = counts + 13;
			i = 0;
			do
			{
				while (*(--probe) == 0)
					/* search */ ;

			} while (++i < n);

			if (aceLowStraight)
				if (n < 4)
					return (uint8)(5 - n);			// 5, then 4, then 3, then 2
				else
					return (uint8)(CardMgr::ACE-2);	// finally, ACE

			return (uint8)((probe - counts) + 2);
		}

		case HR_FLUSH:
		case HR_HIGHCARD:
		default:
		{
			int32 whichSingle = 4 - n;
			uint8* probe2;

			probe = (uint8*)memchr(counts, 1, 13);
			while (whichSingle--)
			{
				probe2 = (uint8*)memchr(probe+1, 1, 13 - (probe-counts+1));
				probe = probe2;
			}

			return (uint8)((probe - counts) + 2);
		}
	}
}
